{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# 什么是推导式？都有哪些类型的推导式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个问题只是简单的问了推导式的概念，所以回答出概念即可。但是，在 Python 面试过程中推导式相关问题可以说是层出不穷，所以掌握推导式的运用对我们来说还是非常重要的。先来看下这道题的标准答案：\n",
    "\n",
    "**答案**  \n",
    "推导式 (comprehensions)，是Python的一种独有特性。**推导式是可以从一个可迭代的数据结构构建出另一个新的数据结构**。主要有**列表推导式，字典推导式和集合推导式**。 Python2 和 Python3 中都支持推导式语法。\n",
    "\n",
    "**拓展：列表推导，字典推导，集合推导的运用**  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 列表推导"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表推导可以帮助我们把一个序列或是其他可迭代对象中的元素过滤或是加工，然后再创建一个新的列表。它的语法是这样的：   \n",
    "**[x_expr for x in iterable if condition ]**\n",
    "\n",
    "\n",
    "**x_expr** : 为新的列表中的元素，这里可以是一个有返回值的**函数**，函数的返回值即为新列表的元素。  \n",
    "**for x in iterable** : iterable 可迭代对象  \n",
    "**if condition** : 根据条件过滤  \n",
    "只看语法的话肯定不足以体现推导式的魅力，我们来做个案例。\n",
    "\n",
    "有一个列表 squares = [1,2,3,4,5],我们想得到这个**列表中每一个元素的平方所组成的一个新的列表**，那么该怎么办呢？有三种方法：for 语句，匿名函数，列表推导式，我们逐一来看下这三种方法哪一种比较简洁。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 4, 9, 16, 25]\n",
      "[1, 4, 9, 16, 25]\n",
      "[1, 4, 9, 16, 25]\n"
     ]
    }
   ],
   "source": [
    "squares = [1,2,3,4,5]\n",
    "# 第一种方法：for 语句\n",
    "new_squares = []\n",
    "for x in squares:\n",
    "    new_squares.append(x**2)\n",
    "print (new_squares)\n",
    "    \n",
    "# 第二种方法：匿名函数\n",
    "new_squares = list(map(lambda x: x**2, squares))\n",
    "print (new_squares)\n",
    "\n",
    "# 第三种方法：列表推导式\n",
    "new_squares = [x**2 for x in squares]\n",
    "print (new_squares)\n",
    "\n",
    "\n",
    "#从这个案例中我们不难发现，列表推导式的语法是最简洁易懂的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Tips:什么是可迭代对象呢？**\n",
    "\n",
    "**简单来说，如果这个对象可以在for循环进行迭代操作，那么它就是可迭代对象**。在程序运行时，for会调用容器对象中的iter()，而这个函数会返回一个定义了__next__()的迭代器，每迭代一次就调用一次，逐一访问容器中的元素。\n",
    "\n",
    "根据Python迭代协议，首先检查对象是否实现了__iter__方法，如果存在，for语句会获取一个迭代器，这个迭代器实现了__next__()方法以允许对容器对象的条目进行迭代操作。另外一种情况是，这个对象没有实现__iter__方法，而只是实现了__getitem__这个方法，并且该方法的参数是从0开始的整型参数，就像下面这个例子一样。\n",
    "\n",
    "关于迭代器的相关知识有兴趣的同学可以去百度下相关知识点哦，不过后面的章节也会逐一给大家讲到。\n",
    "\n",
    "列表推导还可以使用多个嵌套的迭代：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1), ('c', 2), ('c', 3)]\n"
     ]
    }
   ],
   "source": [
    "seq1 = 'abc'\n",
    "seq2 = (1, 2, 3)\n",
    "# 这里等同于一个for循环嵌套了另一个for循环，前面的在最外层\n",
    "\n",
    "print([(x, y) for x in seq1 for y in seq2]) \n",
    "\n",
    "#输出结果：[('a', 1), ('a', 2), ('a', 3),('b', 1), ('b', 2), ('b', 3),('c', 1), ('c', 2), ('c', 3)]\n",
    "\n",
    "#Tips: 注意这里的语法，x，y必须用括号括起来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 字典推导"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**字典推导可以从任何以键值对作为元素的可迭代对象将其元素进行过滤或是加工,然后再新建构建出一个字典。其语法基本和列表推导式类似,只不过最外层的 [] 要换成 {}。**\n",
    "\n",
    "下面演示一些常用的字典推导的例子：\n",
    "\n",
    "将列表转换为字典，用数字索引作为关键字。\n",
    "\n",
    "d = { i:v for i, v in enumerate(['a', 'b', 'c'])}  \n",
    "#使用zip将多个可迭代对象聚合成一个新的迭代对象。\n",
    "\n",
    "names = ['paul', 'adam', 'jane']  \n",
    "ages = [20, 24, 29]  \n",
    "d = { name: age for name, age in zip(names, ages)}  \n",
    "Tips: enumerate和zip这两个内置函数在面试问题中会经常使用到，所以我建议大家熟练掌握这个两个函数的调用。后面的章节中也会有这两个函数的讲解。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 集合推导"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "集合推导有点类似于列表推导，把一个序列或是其他可迭代对象中的元素过滤或是加工然后构建出一个集合。\n",
    "\n",
    "Tips: 另外需要注意，集合推导和列表推导在句法上唯一的区别是括号的不同。还有一个容易混淆的是生成器表达式，也只是括号的不同。集合推导是花括号，列表推导是方括号，生成器表达式是圆括号。这里再提一句，生成器表达式相对于列表推导式的优势在于，它得到的是一个生成器，只有在需要的时候，才会计算新的值，从而节省内存。比如，你如果在Python交互界面上输出一个从列表推导式得到的一个新列表对象，它会把这个对象中的所有元素打印出来，所以它的所有条目都已经在内存中。但是如果从生成器表达式得到的生成器对象，需要通过调用next(gen)方法去手动遍历。\n",
    "\n",
    "**小结**  \n",
    "Python主要内置类型有：数字，序列，映射，集合，类，实例和异常。1.3、1.4、1.5 这三个小节的3个问题主要围绕序列，映射和集合的基本操作来考察面试者的一些基本功，也从侧面去考察面试者的编程素养，比如，捕获异常。另外，在这一节中还列举了很多特殊方法的使用，在后面的章节中，我还会针对特殊方法做进一步的讲解。最后再重复一下本节的几个必须掌握的知识点：\n",
    "\n",
    "**序列的索引与切片**  \n",
    "映射的删除键值\n",
    "容器对象，对不正确的key会引发TypeError，如果是序列，key为序列索引集范围以外的值会引发IndexError，如果是映射，key值不存在，会引发KeyError。\n",
    "常用类型推导式\n",
    "可迭代对象存在的要素：定义了__iter__方法或__getitem__(index)方法，并且index为从0开始的整数索引。\n",
    "这些内容涉及到容器类的一些常用特殊方法：\n",
    "\n",
    "obj.__getitem__(key)，调用此方法以实现obj[key]的求值。\n",
    "obj.__delitem__(key)，调用此方法以实现obj[key]的删除。\n",
    "obj.__iter__()，此方法在需要为容器创建迭代器时被调用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
